use std::hash::Hash;
use std::marker::PhantomData;
use std::fmt::Debug;

use config::Config;
use serde::{Deserialize, Serialize};
use sha::predule::*;
use sha::mh_sha1::{IsalMhSha1Ctx, MhSha1BlockFunction};
use sha::mh_sha256::{IsalMhSha256Ctx, MhSha256BlockFunction};

pub const FINGERPRINT_CONFIG_NAME:&str = "fingerprint";

#[cfg(any(target_arch="x86", target_arch="x86_64"))]
pub use x86::*;
#[cfg(all(any(target_arch="arm", target_arch="aarch64")))]
pub use arm::*;

pub trait FingerPrint:Sized {
    fn fp_len(&self) -> usize;
    fn digest(&self, data:&[u8], fp:&mut [u8]);
}

#[cfg(any(target_arch="x86", target_arch="x86_64"))]
mod x86 {
    use super::*;
    use std::default::Default;
    #[allow(non_camel_case_types)]
    #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
    pub enum FingerPrintBuilder {
        sha1,
        sha256,
        //mh_sha1
        mh_sha1,
        mh_sha1_sse,
        mh_sha1_avx,
        mh_sha1_avx2,
        mh_sha1_avx512,
        //mh_sha256,
        mh_sha256,
        mh_sha256_sse,
        mh_sha256_avx,
        mh_sha256_avx2,
        mh_sha256_avx512,
    }

    impl FingerPrintBuilder {
        pub fn build(self) -> FingerPrintConfig {
            match self {
                FingerPrintBuilder::sha1 => FingerPrintConfig::Sha1(Sha1Fp::default()),
                FingerPrintBuilder::sha256 => FingerPrintConfig::Sha256(Sha1Fp::default()),
                
                // mh_sha1 variants
                FingerPrintBuilder::mh_sha1 => FingerPrintConfig::MhSha1Base(MhSha1Fp::<sha::mh_sha1::MhSha1Base>::default()),
                FingerPrintBuilder::mh_sha1_sse => FingerPrintConfig::MhSha1Sse(MhSha1Fp::<sha::mh_sha1::MhSha1Sse>::default()),
                FingerPrintBuilder::mh_sha1_avx => FingerPrintConfig::MhSha1Avx(MhSha1Fp::<sha::mh_sha1::MhSha1Avx>::default()),
                FingerPrintBuilder::mh_sha1_avx2 => FingerPrintConfig::MhSha1Avx2(MhSha1Fp::<sha::mh_sha1::MhSha1Avx2>::default()),
                FingerPrintBuilder::mh_sha1_avx512 => FingerPrintConfig::MhSha1Avx512(MhSha1Fp::<sha::mh_sha1::MhSha1Avx512>::default()),
                
                // mh_sha256 variants
                FingerPrintBuilder::mh_sha256 => FingerPrintConfig::MhSha256Base(MhSha256Fp::<sha::mh_sha256::MhSha256Base>::default()),
                FingerPrintBuilder::mh_sha256_sse => FingerPrintConfig::MhSha256Sse(MhSha256Fp::<sha::mh_sha256::MhSha256Sse>::default()),
                FingerPrintBuilder::mh_sha256_avx => FingerPrintConfig::MhSha256Avx(MhSha256Fp::<sha::mh_sha256::MhSha256Avx>::default()),
                FingerPrintBuilder::mh_sha256_avx2 => FingerPrintConfig::MhSha256Avx2(MhSha256Fp::<sha::mh_sha256::MhSha256Avx2>::default()),
                FingerPrintBuilder::mh_sha256_avx512 => FingerPrintConfig::MhSha256Avx52562(MhSha256Fp::<sha::mh_sha256::MhSha256Avx512>::default()),
            }
        }
    }
    
    #[cfg(any(target_arch="x86", target_arch="x86_64"))]
    #[impl_enum::with_methods {
        pub fn fp_len(&self) -> usize
        pub fn digest(&self, data:&[u8], fp:&mut [u8])
    }]
    #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
    pub enum FingerPrintConfig {
        Sha1(Sha1Fp),
        Sha256(Sha1Fp),
        // mh_sha1
        MhSha1Base(MhSha1Fp<sha::mh_sha1::MhSha1Base>),
        MhSha1Sse(MhSha1Fp<sha::mh_sha1::MhSha1Sse>),
        MhSha1Avx(MhSha1Fp<sha::mh_sha1::MhSha1Avx>),
        MhSha1Avx2(MhSha1Fp<sha::mh_sha1::MhSha1Avx2>),
        MhSha1Avx512(MhSha1Fp<sha::mh_sha1::MhSha1Avx512>),
    
        // mh_sha256
        MhSha256Base(MhSha256Fp<sha::mh_sha256::MhSha256Base>),
        MhSha256Sse(MhSha256Fp<sha::mh_sha256::MhSha256Sse>),
        MhSha256Avx(MhSha256Fp<sha::mh_sha256::MhSha256Avx>),
        MhSha256Avx2(MhSha256Fp<sha::mh_sha256::MhSha256Avx2>),
        MhSha256Avx52562(MhSha256Fp<sha::mh_sha256::MhSha256Avx512>),
    }

    impl FingerPrintConfig {
        pub fn from_config(config:&Config) -> Option<Self> {
            let builder:FingerPrintBuilder = config.get(FINGERPRINT_CONFIG_NAME).ok()?;
            Some(builder.build())
        }
    }

    impl Default for FingerPrintConfig {
        fn default() -> Self {
            Self::Sha1(Sha1Fp)
        }
    }
    
    impl FingerPrintConfig {
        pub fn fp_of(&self, data:&[u8]) -> Vec<u8> {
            let mut fp = vec![0;self.fp_len()];
            self.digest(data, &mut fp);
            fp
        }
    }
}

#[cfg(all(any(target_arch="arm", target_arch="aarch64")))]
mod arm {
    #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
    pub enum FingerPrintBuilder {
        sha1,
        sha256,
        // mh_sha1
        mh_sha1,
        mh_sha1_ce,
        mh_sha1_asimd,
        mh_sha1_sve,
        mh_sha1_sve2,
        // mh_sha256
        mh_sha256,
        mh_sha256_ce,
    }

    impl FingerPrintBuilder {
        pub fn build(self) -> FingerPrintConfig {
            match self {
                FingerPrintBuilder::sha1 => FingerPrintConfig::Sha1(Sha1Fp::default()),
                FingerPrintBuilder::sha256 => FingerPrintConfig::Sha256(Sha1Fp::default()),
                
                // mh_sha1 variants
                FingerPrintBuilder::mh_sha1 => FingerPrintConfig::MhSha1Base(MhSha1Fp::<sha::mh_sha1::MhSha1Base>::default()),
                FingerPrintBuilder::mh_sha1_ce => FingerPrintConfig::MhSha1Ce(MhSha1Fp::<sha::mh_sha1::MhSha1Ce>::default()),
                FingerPrintBuilder::mh_sha1_asimd => FingerPrintConfig::MhSha1Asimd(MhSha1Fp::<sha::mh_sha1::MhSha1Asimd>::default()),
                FingerPrintBuilder::mh_sha1_sve => FingerPrintConfig::MhSha1Sve(MhSha1Fp::<sha::mh_sha1::MhSha1Sve>::default()),
                FingerPrintBuilder::mh_sha1_sve2 => FingerPrintConfig::MhSha1Sve2(MhSha1Fp::<sha::mh_sha1::MhSha1Sve2>::default()),
                
                // mh_sha256 variants
                FingerPrintBuilder::mh_sha256 => FingerPrintConfig::MhSha256Base(MhSha256Fp::<sha::mh_sha256::MhSha256Base>::default()),
                FingerPrintBuilder::mh_sha256_ce => FingerPrintConfig::MhSha256Ce(MhSha256Fp::<sha::mh_sha256::MhSha256Ce>::default()),
            }
        }
    }

    #[impl_enum::with_methods {
        pub fn fp_len(&self) -> usize
        pub fn digest(&self, data:&[u8], fp:&mut [u8])
    }]
    #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
    pub enum FingerPrintConfig {
        Sha1(Sha1Fp),
        Sha256(Sha1Fp),
        // mh_sha1
        MhSha1Base(MhSha1Fp<sha::mh_sha1::MhSha1Base>),
        MhSha1Ce(MhSha1Fp<sha::mh_sha1::MhSha1Ce>),
        MhSha1Asimd(MhSha1Fp<sha::mh_sha1::MhSha1Asimd>),
        MhSha1Sve(MhSha1Fp<sha::mh_sha1::MhSha1Sve>),
        MhSha1Sve2(MhSha1Fp<sha::mh_sha1::MhSha1Sve2>),

        // mh_sha256
        MhSha256Base(MhSha256Fp<sha::mh_sha256::MhSha256Base>),
        MhSha256Ce(MhSha256Fp<sha::mh_sha256::MhSha256Ce>),
    }

    impl FingerPrintConfig {
        pub fn from_config(config:&Config) -> Option<Self> {
            let builder:FingerPrintBuilder = config.get(FINGERPRINT_CONFIG_NAME).ok()?;
            Some(builder.build())
        }
    }

    impl Default for FingerPrintConfig {
        fn default() -> Self {
            Self::Sha1(Sha1Fp)
        }
    }
    
    impl FingerPrintConfig {
        pub fn fp_of(&self, data:&[u8]) -> Vec<u8> {
            let mut fp = vec![0;self.fp_len()];
            self.digest(data, &mut fp);
            fp
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord, Serialize, Default)]
pub struct Sha1Fp;

impl FingerPrint for Sha1Fp {
    #[inline(always)]
    fn fp_len(&self) -> usize { SHA1_U8_LENGTH }
    fn digest(&self, data:&[u8], fp:&mut [u8]) {
        let mut ctx = Sha1Ctx::new();
        ctx.update(data);
        ctx.finalize(fp.try_into().unwrap());
    }
}

impl<'de> Deserialize<'de> for Sha1Fp {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
        where
            D: serde::Deserializer<'de> {
        Ok(Self)
    }
}

#[derive(Debug, Clone, Copy, Serialize, Default)]
pub struct Sha256Fp;

impl FingerPrint for Sha256Fp {
    #[inline(always)]
    fn fp_len(&self) -> usize { SHA256_U8_LENGTH }
    fn digest(&self, data:&[u8], fp:&mut [u8]) {
        let mut ctx = Sha256Ctx::new();
        ctx.update(data);
        ctx.finalize(fp.try_into().unwrap());
    }
}

impl<'de> Deserialize<'de> for Sha256Fp {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
        where
            D: serde::Deserializer<'de> {
        Ok(Self)
    }
}

#[derive(Debug, Clone, Copy, Serialize, Default)]
pub struct MhSha1Fp<T:MhSha1BlockFunction>(PhantomData<T>);

impl<T:MhSha1BlockFunction> FingerPrint for MhSha1Fp<T> {
    #[inline(always)]
    fn fp_len(&self) -> usize { MH_SHA1_U8_LENGTH }
    fn digest(&self, data:&[u8], fp:&mut [u8]) {
        let mut ctx = IsalMhSha1Ctx::<T>::new();
        ctx.update(data);
        ctx.finalize(fp.try_into().unwrap());
    }
}

impl<'de, T:MhSha1BlockFunction> Deserialize<'de> for MhSha1Fp<T> {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
        where
            D: serde::Deserializer<'de> {
        Ok(Self(std::marker::PhantomData::default()))
    }
}


#[derive(Debug, Clone, Copy, Serialize, Default)]
pub struct MhSha256Fp<T:MhSha256BlockFunction>(PhantomData<T>);

impl<T:MhSha256BlockFunction> FingerPrint for MhSha256Fp<T> {
    #[inline(always)]
    fn fp_len(&self) -> usize { MH_SHA256_U8_LENGTH }
    fn digest(&self, data:&[u8], fp:&mut [u8]) {
        let mut ctx = IsalMhSha256Ctx::<T>::new();
        ctx.update(data);
        ctx.finalize(fp.try_into().unwrap());
    }
}

impl<'de, T:MhSha256BlockFunction> Deserialize<'de> for MhSha256Fp<T> {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
        where
            D: serde::Deserializer<'de> {
        Ok(Self(std::marker::PhantomData::default()))
    }
}